#include "trancevideo.h"

TranceVideo::TranceVideo()
{
    //1.注册组件
    av_register_all();
    this->videoIndex = -1;
}

void TranceVideo::openH264File(QString filepath)
{
    this->inFormatContext = avformat_alloc_context();
    this->outFormatContext = avformat_alloc_context();
    this->packet = (AVPacket*)malloc(sizeof(AVPacket));
    //2.打开一个H264文件(源文件已经存在的文件)
    int res = avformat_open_input(&this->inFormatContext, filepath.toStdString().c_str(), nullptr, nullptr);
    if(res!=0)
    {
        qDebug() << "open video fail";
    }
    else {
        qDebug() << "open video success";
    }
    //3.获取码流数据
    res = avformat_find_stream_info(this->inFormatContext, nullptr);
    if(res<0){
        qDebug() << "find_stream_info fail";
    }
    else{
        qDebug() << "find_stream_info success";
    }
    //4.判断上一步获取到的码流数据是否为视频流
    for(int i=0;i<inFormatContext->nb_streams;i++){
        //nb_streams 有多少种码流数据的信息
        if(inFormatContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            videoIndex = i;
            break;
        }
    }
    if(this->videoIndex==-1)
    {
        qDebug() << "video not find";
    }
    else{
        qDebug() << "video find, this->videoIndex" << this->videoIndex;
    }
    qDebug()<<"视频总帧数"<<this->inFormatContext->streams[this->videoIndex]->nb_frames;//后面有用
}

void TranceVideo::tranceToMp4(QString filepath)
{
    //5.创建并打开新的转码目标文件
    this->outputFormat = av_guess_format(nullptr, filepath.toStdString().c_str(), nullptr);
    if(this->outputFormat==nullptr){
        qDebug() << "format error";
    }
    else {
        qDebug() << "format success";
    }
    //5-1.设置格式
    this->outFormatContext->oformat = this->outputFormat;

    //6-1.打开一个新的图像视频流
    int res = avio_open(&(this->outFormatContext->pb), filepath.toStdString().c_str(), AVIO_FLAG_WRITE);
    if(res<0)
    {
        qDebug() << "avio_open error";
    }
    else {
        qDebug() << "avio_open success";
    }
    //6-2.新建视频流
    AVStream * newStream = avformat_new_stream(this->outFormatContext, nullptr);
    if(newStream==nullptr)
    {
        qDebug() << "avformat_new_stream error";
    }
    else {
        qDebug() << "avformat_new_stream_ success";
    }
    //6-3.视频流设置
    avcodec_parameters_copy(newStream->codecpar, this->inFormatContext->streams[this->videoIndex]->codecpar);
    newStream->codecpar->codec_tag = 0; //没有设置会编码失败
    //6-4.写头帧数据
    res = avformat_write_header(this->outFormatContext, nullptr);
    if(res<0)
    {
        qDebug() << "avformat_write_header error";
    }
    else {
        qDebug() << "avformat_write_header success";
    }

    //6-5生成一个新的码流数据据AVPacket
    int size = newStream->codecpar->width * newStream->codecpar->height;
    res = av_new_packet(this->packet,size);
    if(res!=0)
    {
        qDebug() << "av_new_packet error";
    }
    else {
        qDebug() << "av_new_packet success";
    }

    //7.计算帧数
    int frameCount = 0;
    //从原先的文件当中读
    while(av_read_frame(this->inFormatContext, this->packet)==0)
    {
        qDebug() << "this->packet->stream_index= " << this->packet->stream_index;
        if(this->packet->stream_index==this->videoIndex)
        {
            //读到的是视频流，就++
            frameCount++;
            qDebug() << "frameCount = " << frameCount;
            /*
             * I帧：关键帧
             * P帧：预测帧
             * B真
             */
            if(this->packet->pts==AV_NOPTS_VALUE){
                //原视频文件时间基
                AVRational timebase1 = this->inFormatContext->streams[this->videoIndex]->time_base;
                //计算两帧之间的时长
                int64_t duration = (double)AV_TIME_BASE/
                        (double)av_q2d(this->inFormatContext->streams[this->videoIndex]->r_frame_rate);
                //计算显示时间基：帧数 * 两帧之间的时长/输入的时间基*AV_TIME_BASE
                this->packet->pts = (double)(frameCount*duration)/
                        (double)(av_q2d (timebase1)*AV_TIME_BASE);

                //解码时间基
                this->packet->dts = this->packet->pts;

                //时长
                this->packet->duration = (double)duration/(double)(av_q2d(timebase1)*AV_TIME_BASE);
            }
            else {
                qDebug() << "frameCount" << frameCount;
                continue;
            }

            //解码时间基转换
            /*
             * 参数1：需要转换值
             * 参数2：原视频时间基
             * 参数3：转码的视频时间基
             * 参数4：算法 转转规则
             *
             */
            this->packet->dts = av_rescale_q_rnd(this->packet->dts,
                        this->inFormatContext->streams[this->videoIndex]->time_base,
                        newStream->time_base,
                        (AVRounding)(AV_ROUND_INF|AV_ROUND_PASS_MINMAX));

            //显示时间基
            this->packet->pts = av_rescale_q_rnd(this->packet->pts,
                                                 this->inFormatContext->streams[this->videoIndex]->time_base,
                                                 newStream->time_base,
                                                 (AVRounding)(AV_ROUND_INF|AV_ROUND_PASS_MINMAX));
            //两帧之间的帧间时长
            this->packet->duration = av_rescale_q(this->packet->duration,
                                              this->inFormatContext->streams[this->videoIndex]->time_base,//原视频时间基
                                              newStream->time_base);    //新视频时间基
            //其他数据设置
            this->packet->pos = -1;
            //标志位用来识别关键帧    或等于
            this->packet->flags |=AV_PKT_FLAG_KEY;
            //AVPacket 在码流数据数组中的下标
            this->packet->stream_index = 0;

            //写一帧
            av_interleaved_write_frame(this->outFormatContext, this->packet);

            emit sendS();
        }

        //重置码流数据
        av_packet_unref(this->packet);
    }
    //写尾帧数据
    av_write_trailer(this->outFormatContext);
    //关闭流
    avio_close(this->outFormatContext->pb);
    //释放文件上下文对象
    av_free(this->outFormatContext);
    //输入文件关闭
    avformat_close_input(&this->inFormatContext);
    //释放输入文件上下文对象
    av_free(this->inFormatContext);

    qDebug() << "write MP4 file success";
}

void TranceVideo::run()
{

}
